package com.eternal.main.model;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.ClipData;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.databinding.ObservableArrayList;
import androidx.databinding.ObservableList;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;

import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.callback.NavCallback;
import com.alibaba.android.arouter.launcher.ARouter;
import com.clj.fastble.data.BleDevice;
import com.eternal.base.concat.BluetoothEvent;
import com.eternal.base.concat.DeviceInfo;
import com.eternal.base.concat.DeviceTFP;
import com.eternal.base.concat.PortInfo;
import com.eternal.base.data.DeviceRepository;
import com.eternal.base.data.ble.BleServer;
import com.eternal.base.data.ble.BleStatue;
import com.eternal.base.database.entity.Device;
import com.eternal.base.global.ActivityEvent;
import com.eternal.base.global.ProgressEvent;
import com.eternal.base.protocol.ProtocolResolution;
import com.eternal.base.protocol.ProtocolTransformer;
import com.eternal.base.global.BluetoothKey;
import com.eternal.base.router.RouterActivityPath;
import com.eternal.base.utils.CustomToastUtils;
import com.eternal.base.utils.TimeUtil;
import com.eternal.framework.binding.command.BindingAction;
import com.eternal.framework.binding.command.BindingCommand;
import com.eternal.framework.binding.command.BindingConsumer;
import com.eternal.framework.bus.Messenger;
import com.eternal.framework.bus.RxBus;
import com.eternal.framework.component.AppManager;
import com.eternal.framework.component.BaseViewModel;
import com.eternal.framework.utils.KLog;
import com.eternal.framework.utils.RxUtils;
import com.eternal.framework.utils.Utils;
import com.eternal.main.BR;
import com.eternal.main.MainActivity;
import com.eternal.main.R;
import com.tbruyelle.rxpermissions2.RxPermissions;

import org.reactivestreams.Publisher;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import me.tatarka.bindingcollectionadapter2.ItemBinding;

import static com.eternal.framework.utils.Utils.getContext;
import static com.eternal.framework.utils.Utils.getString;

/**
 * Created by Administrator
 * On 2020/4/23
 * Description:
 *
 * @author Administrator
 */
public class MainModel extends BaseViewModel {
    static final String CHOOSE_PORT = "choose port";

    private DeviceRepository repository;
    private Disposable scan, timeSubs, refresh, refreshE, connect;
    private List<ItemModel> connected;
    List<BleDevice> scanBleDevices;
    private CompositeDisposable composite;
    private boolean isUserVisibleHint;
    private boolean pageAddVisible;
    private DeviceInfo selectedDevice;
    private int currentState;

    public MainModel(@NonNull Application application) {
        super(application);
        connected = new ArrayList<>();
        scanBleDevices = new ArrayList<>();
        composite = new CompositeDisposable();
    }

    public MutableLiveData<String> title = new MutableLiveData<>();
    public MutableLiveData<Boolean> showTip = new MutableLiveData<>();

    public BindingCommand<Void> onAbout = new BindingCommand<>(new BindingAction() {
        @Override
        public void call() {
            ARouter.getInstance().build(RouterActivityPath.About.PAGE_ABOUT)
                    .withTransition(R.anim.left_in, R.anim.right_out)
                    .navigation(AppManager.getAppManager().currentActivity());
        }
    });

    public BindingCommand<Void> onAdd = new BindingCommand<>(new BindingAction() {
        @Override
        public void call() {
            pageAddVisible = true;
            stopScan();
            ARouter.getInstance().build(RouterActivityPath.Device.PAGE_CHOOSE)
                    .withTransition(R.anim.right_in, R.anim.left_out)
                    .navigation(AppManager.getAppManager().currentActivity());
        }
    });

    public ObservableList<ItemModel> items = new ObservableArrayList<>();

    public ItemBinding<ItemModel> itemBinding = ItemBinding.of(BR.item, R.layout.item_main);

    public void init(DeviceRepository repository) {
        this.repository = repository;
        title.setValue("Devices");
        initPermission();
        initDeviceAndConnect();
        registerEventBus();
        initMessenger();
        connectDevices();
    }

    private void connectDevices() {
        if (connect == null || connect.isDisposed()) {
            connect = Observable.create(new ObservableOnSubscribe<List<BleDevice>>() {
                @SuppressLint("CheckResult")
                @Override
                public void subscribe(@io.reactivex.annotations.NonNull final ObservableEmitter<List<BleDevice>> emitter) throws Exception {
                    if (pageAddVisible || (scanBleDevices == null || scanBleDevices.size() == 0)) {
                        emitter.onComplete();
                        return;
                    }
                    List<BleDevice> tempList = new ArrayList<>();
                    for (BleDevice device : scanBleDevices) {
                        if (!repository.isConnect(device.getMac())) {
                            tempList.add(device);
//                            Device info = ProtocolResolution.parseScanRecordData(device);
//                            if (info != null) {
//                                if (info.type < 0x3) {
//                                    tempList.add(device);
//                                } else {
//                                    if (currentState != ActivityEvent.STATE_BACKGROUND) {
//                                        tempList.add(device);
//                                    }
//                                }
//                            }
                        }
                    }
                    scanBleDevices.clear();
                    if (tempList.size() == 0) {
                        emitter.onComplete();
                        return;
                    }
                    if (selectedDevice != null) {
                        BleDevice temp = null;
                        Iterator<BleDevice> iter = tempList.iterator();
                        while (iter.hasNext()) {
                            temp = iter.next();
                            if (temp.getMac().equals(selectedDevice.mac)) {
                                iter.remove();
                                break;
                            }
                            temp = null;
                        }
                        if (temp != null) {
                            tempList.add(0, temp);
                        }
                    }

                    repository.tryConnect(tempList).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {
                        @Override
                        public void accept(String s) throws Exception {
                            for (ItemModel item : items) {
                                if (item.getMac().equals(s)) {
                                    item.connect(System.currentTimeMillis());
                                    connected.add(item);
                                    stop();
                                    syncTimeAndStart();
                                    RxBus.getDefault().post(new BluetoothEvent(BluetoothEvent.CONNECT, s));
                                    break;
                                }
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            KLog.d("MainModel connectDevices Consumer<Throwable>");
                            emitter.onComplete();
                        }
                    }, new Action() {
                        @Override
                        public void run() throws Exception {
                            KLog.d("MainModel connectDevices Action Exception ");
                            emitter.onComplete();
                        }
                    });
                }
            }).subscribeOn(Schedulers.io()).repeatWhen(new Function<Observable<Object>, ObservableSource<?>>() {
                @Override
                public ObservableSource<?> apply(@io.reactivex.annotations.NonNull Observable<Object> objectObservable) throws Exception {
                    return objectObservable.delay(500, TimeUnit.MILLISECONDS);
                }
            }).subscribe();
        }
    }

    @SuppressLint("CheckResult")
    private void initDeviceAndConnect() {
        Disposable temp = repository.getInfo().subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<List<DeviceInfo>>() {
                    @Override
                    public void accept(List<DeviceInfo> devices) {
                        if (devices.isEmpty()) {
                            showTip.setValue(true);
                        } else {
                            showTip.setValue(false);
                            ItemModel model;
                            List<DeviceInfo> tempList = new ArrayList<>();
                            for (DeviceInfo deviceInfo : devices) {
                                if (deviceInfo.port == 0) {
                                    tempList.add(deviceInfo);
                                }
                            }
                            for (DeviceInfo info1 : tempList) {
                                if (info1.type == BluetoothKey.DEVICE_TYPE_E) {
                                    List<PortInfo> portInfos = new ArrayList<>();
                                    for (DeviceInfo info2 : devices) {
                                        if (info2.port != 0 && info2.mac.equals(info1.mac)) {
                                            PortInfo portInfo = new PortInfo();
                                            portInfo.id = info2.port;
                                            portInfo.name = info2.name;
                                            portInfo.fan = info2.fan;
                                            portInfo.fanType = info2.fanType;
                                            portInfo.fanState = info2.fanState;
                                            portInfo.isPlug = info2.isPlug;
                                            portInfos.add(portInfo);
                                        }
                                    }
                                    info1.portList = portInfos;
                                }
                            }
                            devices = tempList;
                            if (devices.size() == items.size()) {
                                for (int i = 0; i < devices.size(); i++) {
                                    items.get(i).update(devices.get(i).portList);
                                    items.get(i).update(devices.get(i).name);
                                }
                            } else if (items.size() < devices.size()) {
                                boolean first = items.isEmpty();
                                items.clear();
                                connected.clear();
                                for (DeviceInfo device : devices) {
                                    model = new ItemModel(device);
                                    if (repository.isConnect(device.mac)) {
                                        connected.add(model);
                                        model.connect(System.currentTimeMillis());
                                    }
                                    items.add(model);
                                }
                                if (refresh != null && !connected.isEmpty()) {
                                    stop();
                                    KLog.e("reset tfp refresh");
                                    syncTimeAndStart();
                                }
                                if (scan == null && first) {
                                    scanDevice();
                                }
                            }
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) {
                        CustomToastUtils.showCenterRoundRectToast(getContext(), getString(R.string.err_database));
                        KLog.e(throwable);
                    }
                });
        addSubscribe(temp);
    }

    @SuppressLint("CheckResult")
    private void scanDevice() {
        if ((scan == null || scan.isDisposed()) && !items.isEmpty()) {
            BleServer.getInstance().enable();
//            repository.clean();
            scan = repository.scanCycle().subscribeOn(Schedulers.io()).observeOn(Schedulers.io())
                    .subscribe(new Consumer<List<BleDevice>>() {
                                   @Override
                                   public void accept(List<BleDevice> bleDevices) {
                                       for (BleDevice bleDevice : bleDevices) {
                                           for (ItemModel itemModel : items) {
                                               if (bleDevice.getMac().equals(itemModel.getMac()) && !isContainer(scanBleDevices, bleDevice.getMac())) {
                                                   scanBleDevices.add(bleDevice);
                                                   continue;
                                               }
                                           }
                                       }
                                   }
                               }, new Consumer<Throwable>() {
                                   @Override
                                   public void accept(Throwable throwable) throws Exception {
                                       throwable.printStackTrace();
                                       KLog.e(throwable);
                                   }
                               }
                    );
        }
    }

    private boolean isContainer(List<BleDevice> list, String mac) {
        for (BleDevice device : list) {
            if (device.getMac().equals(mac)) {
                return true;
            }
        }
        return false;
    }

    public void stopScan() {
        repository.closeScan();
        if (connect != null) {
            if (!connect.isDisposed()) {
                connect.dispose();
            }
        }
        if (scan != null) {
            if (!scan.isDisposed()) {
                scan.dispose();
            }
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void registerNotify() {
        pageAddVisible = false;
        isUserVisibleHint = true;
        connectDevices();
        scanDevice();
        timeSubs = RxBus.getDefault().toObservable(Long.class)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long time) {
                        for (ItemModel item : connected) {
                            item.time.set(Utils.getString(R.string.tip_connect, ProtocolTransformer.getTime(time)));
                        }
                    }
                });
        for (ItemModel item : connected) {
            item.time.set(Utils.getString(R.string.tip_connect, ProtocolTransformer.getTime(System.currentTimeMillis())));
        }
        if (refresh == null && !connected.isEmpty()) {
            KLog.i("start refresh tfp");
            syncTimeAndStart();
        }
        stopE();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void unregisterNotify() {
        isUserVisibleHint = false;
        if (timeSubs != null) {
            timeSubs.dispose();
            timeSubs = null;
        }
        stop();
    }

    public void registerEventBus() {
//        addSubscribe(RxBus.getDefault()
//                .toObservable(ActivityEvent.class)
//                .subscribe(new Consumer<ActivityEvent>() {
//                    @Override
//                    public void accept(ActivityEvent event) {
//                        if(event.what==ActivityEvent.STATE_BACKGROUND){
//                            // 断开C家族连接
//                            currentState = ActivityEvent.STATE_BACKGROUND;
//                            addSubscribe(repository.disConnectC()
//                                    .subscribeOn(Schedulers.io())
//                                    .observeOn(AndroidSchedulers.mainThread())
//                                    .subscribe(new Action() {
//                                        @Override
//                                        public void run() {
//                                        }
//                                    }));
//                        }else if(event.what==ActivityEvent.STATE_FOREGROUND){
//                            currentState = ActivityEvent.STATE_FOREGROUND;
//                        }
//                    }
//                }));

        addSubscribe(RxBus.getDefault().toObservable(BluetoothEvent.class).subscribe(new Consumer<BluetoothEvent>() {
            @Override
            public void accept(BluetoothEvent event) throws Exception {
                if (event.what == BluetoothEvent.DISCONNECT) {
                    String mac = (String) event.obj;
                    ItemModel temp;
                    Iterator<ItemModel> iter = connected.iterator();
                    while (iter.hasNext()) {
                        temp = iter.next();
                        if (temp.getMac().equals(mac)) {
                            temp.disconnect();
                            iter.remove();
                            break;
                        }
                    }
                } else if (event.what == BluetoothEvent.STATE_OFF) {
                    stopScan();
                } else if (event.what == BluetoothEvent.STATE_ON) {
                    if (!pageAddVisible) {
                        scanDevice();
                        connectDevices();
                    }
                }
            }
        }));
    }

    public void requestConnect(int index) {
        if (index != -1) {
            ItemModel item = items.get(index);
            connected.add(item);
            item.connect(System.currentTimeMillis());
        }
    }

    private void initPermission() {
        addSubscribe(new RxPermissions(((FragmentActivity) AppManager.getAppManager().currentActivity()))
                .request(Manifest.permission.BLUETOOTH,
                        Manifest.permission.ACCESS_COARSE_LOCATION,
                        Manifest.permission.ACCESS_FINE_LOCATION)
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        if (!aBoolean) {
                            CustomToastUtils.showCenterRoundRectToast(getContext(), getString(R.string.permission_reject));
                        }
                        BleServer.getInstance().enable();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        throwable.printStackTrace();
                        KLog.e(throwable);
                    }
                }));
    }

    private void initMessenger() {
        Messenger.getDefault().register(this, CHOOSE_PORT, ItemModel.class, new BindingConsumer<ItemModel>() {
            @Override
            public void call(ItemModel itemModel) {
                choosePort(itemModel);
            }
        });
    }

    @SuppressLint("CheckResult")
    private void choosePort(final ItemModel itemModel) {
        final BleStatue statue = repository.getConnect(itemModel.getMac());
        if (statue != null) {
            repository.setChoosePort(statue, itemModel.getDeviceInfo().choosePort)
                    .compose(RxUtils.<Boolean>bindToLifecycle(getLifecycleProvider()))
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .doOnSubscribe(new Consumer<Disposable>() {
                        @Override
                        public void accept(Disposable disposable) {
//                            RxBus.getDefault().post(new ProgressEvent(ProgressEvent.START));
                        }
                    })
                    .subscribe(new Consumer<Boolean>() {
                        @Override
                        public void accept(Boolean result) {
                            if (!result) {
                                KLog.e("choose port error");
                            }
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) {
                            throwable.printStackTrace();
                            KLog.e(throwable);

                        }
                    });
        }
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        Messenger.getDefault().unregister(this);
    }

    @SuppressLint("CheckResult")
    public void remove(final ItemModel item) {
        stop();
        addSubscribe(repository.removeDevice(item.getMac())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action() {
                    @Override
                    public void run() {
                        connected.remove(item);
                        items.remove(item);
                        if (!connected.isEmpty()) {
                            KLog.e("reset refresh tfp");
                            syncTimeAndStart();
                        }
                    }
                }));
    }

    @SuppressLint("CheckResult")
    public void remove(final String mac) {
        if (refresh != null) {
            refresh.dispose();
        }
        ItemModel temp;
        Iterator<ItemModel> iter = connected.iterator();
        while (iter.hasNext()) {
            temp = iter.next();
            if (temp.getMac().equals(mac)) {
                iter.remove();
            }
        }
        repository.removeDevice(mac)
                .compose(RxUtils.<Integer>bindToLifecycle(getLifecycleProvider()))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action() {
                    @Override
                    public void run() throws Exception {
                        Iterator<ItemModel> iter = items.iterator();
                        ItemModel temp;
                        while (iter.hasNext()) {
                            temp = iter.next();
                            if (temp.getMac().equals(mac)) {
                                iter.remove();
                                break;
                            }
                        }
                        if (refresh != null && !connected.isEmpty()) {
                            syncTimeAndStart();
                        }
                    }
                });
    }

    private void syncTimeAndStart() {
        if (!isUserVisibleHint) return;
        KLog.i("start refresh ftp");
        List<Single<DeviceTFP>> list = new ArrayList<>();
        for (final ItemModel item : connected) {
            if (item.needRefresh()) {
                list.add(repository.getTFP(item.getMac(), BluetoothKey.COMMAND_GET)
                        .observeOn(AndroidSchedulers.mainThread()).doOnSuccess(new Consumer<DeviceTFP>() {
                            @Override
                            public void accept(DeviceTFP deviceTFP) throws Exception {
                                item.updateTFP(deviceTFP);
                            }
                        }));
            } else {
                composite.add(repository.getTFP(item.getMac()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<DeviceTFP>() {
                    @Override
                    public void accept(DeviceTFP deviceTFP) throws Exception {
                        item.updateTFP(deviceTFP);
                    }
                }));
            }
        }
        refresh = repository.syncTime()
                .andThen(Single.merge(list).repeatWhen(new Function<Flowable<Object>, Publisher<?>>() {
                    @Override
                    public Publisher<?> apply(Flowable<Object> objectFlowable) throws Exception {
                        return objectFlowable.delay(1, TimeUnit.SECONDS);
                    }
                }))
                .ignoreElements()
                .subscribe();
    }

    public void startE() {
        if (selectedDevice == null || selectedDevice.type != BluetoothKey.DEVICE_TYPE_E) {
            return;
        }
        final BleStatue statue = repository.getConnect(selectedDevice.mac);
        if (statue == null) {
            return;
        }
        if (refreshE == null || refreshE.isDisposed()) {
            refreshE = repository.getTFP(selectedDevice.mac, BluetoothKey.COMMAND_GET)
                    .observeOn(AndroidSchedulers.mainThread()).doOnSuccess(new Consumer<DeviceTFP>() {
                        @Override
                        public void accept(DeviceTFP deviceTFP) throws Exception {
                            if (selectedDevice == null) {
                                stopE();
                                return;
                            }
                            //当前选中为Item还是端口，且判断是否为主控模式（主控模式不显示端口）deviceTFP.choosePort=0为主控模式
                            if ((selectedDevice.choosePort == 0 && deviceTFP.choosePort != 0)
                                    || (selectedDevice.choosePort != 0 && deviceTFP.choosePort == 0)) {
                                stopE();
                                pushToMain();
                            } else {
                                if (deviceTFP.portList != null) {
                                    for (PortInfo info : deviceTFP.portList) {
                                        if (info.id == selectedDevice.choosePort && !info.isPlug) {
                                            stopE();
                                            pushToMain();
                                        }
                                    }
                                }
                            }
                        }
                    }).repeatWhen(new Function<Flowable<Object>, Publisher<?>>() {
                        @Override
                        public Publisher<?> apply(Flowable<Object> objectFlowable) throws Exception {
                            return objectFlowable.delay(1, TimeUnit.SECONDS);
                        }
                    }).ignoreElements().subscribe();
        }
    }

    private void stopE() {
        if (refreshE != null) {
            refreshE.dispose();
            refreshE = null;
        }
        if (selectedDevice != null) {
            selectedDevice = null;
        }
    }

    public void setSelectedDevice(DeviceInfo deviceInfo) {
        this.selectedDevice = deviceInfo;
        startE();
    }

    private void stop() {
        if (refresh != null) {
            refresh.dispose();
            refresh = null;
        }
    }

    @SuppressLint("CheckResult")
    public void save() {
        for (ItemModel item : items) {
            if (repository.isConnect(item.getMac())) {
                repository.saveModelInfo(item.getMac(), item.getDeviceInfo());
            }
        }
    }

    public void pushToMain() {
        ARouter.getInstance().build(RouterActivityPath.Main.PAGE_MAIN)
                .withTransition(R.anim.left_in, R.anim.right_out)
                .navigation(AppManager.getAppManager().currentActivity(), new NavCallback() {
                    @Override
                    public void onArrival(Postcard postcard) {
                        AppManager.getAppManager().finishOtherActivity(MainActivity.class);
                    }
                });
    }

}
